home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / disktools / allgemein / val / val.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  41KB  |  1,818 lines

  1. /*
  2.  *    Copyright (C) 1988 Andrew Kemmis
  3.  *
  4.  *    All Rights Reserved
  5.  *
  6.  *    Date:    Jan 1988
  7.  */
  8. #include <ak/stdak.h>
  9.  
  10. char    Program_name[]    = "Val";
  11. int    Version     = 2;
  12. int    MinorVer    = 3;
  13. char    Date[]        = "Dec 1993";
  14. char    CRDate[]    = "1988-93";
  15.  
  16. #include <ak/err.h>
  17. #include <ak/pool.h>
  18. #include <ak/dev.h>
  19. #include <ak/dos.h>
  20. #include <intuition/intuitionbase.h>
  21. #include "val.h"
  22.  
  23. #include "val.qi"
  24.  
  25. #define ULONGS(l)       ((ulong)(l)*(ulong)sizeof(ulong))
  26. #define ZAlloc(a)       PoolAlloc((a), sizeof(*(a)), &GenPoolKey)
  27. #define LAlloc(a,n,z)   {       if (!Seek)\
  28.                     PoolAlloc((a), ULONGS(n), &GenPoolKey);\
  29.                 else\
  30.                     (char *)(a) = AllocMem(ULONGS(n), 0L);\
  31.             }
  32.  
  33. #define ULONGMAX    4294967295
  34.  
  35. extern    char    *strchr();
  36. extern    char    *strrchr();
  37. extern    int    strlen();
  38. extern    int    strcmp();
  39. extern    int    sscanf();
  40.  
  41. extern    char    *AllocMem();
  42.  
  43. int    cli;
  44.  
  45. struct    IBASE    *IBASE = (struct IBASE *)NULL;
  46.  
  47. Static    int    FileSystem;
  48.  
  49. #define IS_OFS    0
  50. #define IS_FFS    1
  51.  
  52. Static    char    StrRoot[] = "Root";
  53. Static    char    StrBitMap[] = "BitMap";
  54. Static    char    StrEtc[] = "Dir,Fil,Lnk";
  55. Static    char    StrDir[] = "Dir";
  56. Static    char    StrFil[] = "Fil";
  57. Static    char    StrExt[] = "Ext";
  58. Static    char    StrDat[] = "Dat";
  59. Static    char    StrSoftLnk[] = "SoftLnk";
  60. Static    char    StrDirLnk[] = "DirLnk";
  61. Static    char    StrFilLnk[] = "FilLnk";
  62. Static    char    StrNextD[] = "NextDat";
  63. Static    char    StrChain[] = "HashChain";
  64. Static    char    StrNext[] = "Next";
  65. Static    char    StrPrev[] = "Prev";
  66. Static    char    StrHuh[] = "Unknown";
  67. Static    struct    AkDevStuff    Stuff = DEVSTUFFINIT;
  68. Static    uchar    *Buffer;
  69. Static    long    Dev = -1;
  70. Static    struct    Names    RootName =
  71.     {    (struct Names *)NULL,   (struct Names *)NULL,   ULONGMAX, };
  72. Static    struct    Links
  73. {    ushort    type;
  74.     ulong    block;
  75.     ulong    prev;
  76.     ulong    next;
  77.     struct    Links    *another;
  78. } *LinkHead, *LinkMaybe;
  79. Static    uchar    *FName;
  80. Static    int    Inhibited = 0;
  81.  
  82. Static    struct    PoolKey BufPoolKey;
  83. Static    struct    PoolKey GenPoolKey;
  84. Static    struct    PoolKey ZerPoolKey;
  85.  
  86. Static    ulong    ReadCount = 0;
  87. Static    struct    Info    *LastInfo;
  88. Static    ulong    LastBlk;
  89. Static    uchar    *MyBitMap;
  90.  
  91. Static    ulong    ReadSmall = 0;
  92. Static    ulong    ReadLarge = 0;
  93. Static    ulong    ReadLNum = 0;
  94. Static    ulong    HitSmall = 0;
  95. Static    ulong    HitLarge = 0;
  96. Static    ulong    MicSta;
  97. Static    ulong    MicFin;
  98. Static    ulong    MicCode;
  99. Static    ulong    MicRead;
  100.  
  101. #define BMAP_TST(n)      (MyBitMap[(n)>>3]&(1<<((n)&7)))
  102. #define BMAP_SET(n)      (MyBitMap[(n)>>3]|=(1<<((n)&7)))
  103.  
  104. Static Copy(d, s, l, siz)
  105. REG    char    *d;
  106. REG    char    *s;
  107. REG    ulong    l;
  108. REG    int    siz;
  109. {
  110.     REG    ulong  len = l * (ulong)siz;
  111.  
  112.     while (len--)
  113.         *(d++) = *(s++);
  114. }
  115.  
  116. Static Get(Rem, num)
  117. REG    struct    Info    *Rem;
  118. REG    ulong    num;
  119. {
  120.     if ((Rem - InfoList) >= MaxDepth)
  121.         MyError(ERR_TOODEEP, MaxDepth);
  122.  
  123.     getblok(num + Stuff.Info.Begin, Rem);
  124. }
  125.  
  126. Static LONG DevRead(num, buf, size)
  127. REG    ulong    num;
  128. REG    ulong    *buf;
  129. REG    LONG    size;
  130. {
  131.     REG    LONG    ret;
  132.     REG    int    retry = Retries + 1;
  133.  
  134.     while (retry-- && (ret = AkDevRead(num, buf, &Stuff, size)))
  135.         ;
  136.  
  137.     if (ret && FailReq)
  138.     {    if (!akrequester("Disk Read Error", "Continue", "Abort"))
  139.         {    puts("Aborted at users request on Read Error");
  140.             Cleanup();
  141.         }
  142.     }
  143.  
  144.     return(ret);
  145. }
  146.  
  147. Static    int    DoCache = !0;
  148.  
  149. #define CACHE_INVALID    -7
  150.  
  151. Static LONG CacheRead(num, buf)
  152. REG    ulong    num;
  153. REG    ulong    **buf;
  154. {
  155.     static    ulong    cache0 = CACHE_INVALID;
  156.     static    ulong    cache1 = CACHE_INVALID;
  157.     static    ulong    cache2 = CACHE_INVALID;
  158.     REG    LONG    ret = 0;
  159.  
  160.     *buf = (ulong *)Buffer;
  161.  
  162.     if (Cache == 1)
  163.         ret = DevRead(num, *buf, BLOCK_SIZE);
  164.     else
  165.     {    if (num == cache0 || num == (cache0 + 1) || (cache1 <= num && num <= cache2))
  166.         {    if (StatCache)
  167.                 MicSta = IBASE->Micros;
  168.  
  169.             if (num == (cache0+1))
  170.                 *buf = (ulong *)&Buffer[BLOCK_SIZE];
  171.             else
  172.             if (num != cache0)
  173.                 *buf = (ulong *)&Buffer[BLOCK_SIZE*(2 + num - cache1)];
  174.  
  175.             if (StatCache)
  176.                 MicFin = IBASE->Micros;
  177.  
  178.             if (StatCache)
  179.             {    if (num == cache0 || num == (cache0+1))
  180.                     HitSmall++;
  181.                 else
  182.                     HitLarge++;
  183.  
  184.                 if (MicSta > MicFin)
  185.                     MicFin += 1000000;
  186.                 MicCode += (MicFin - MicSta);
  187.             }
  188.             if (DebugCache)
  189.             {    if (num == cache0)
  190.                     printf("Hit at :0\n");
  191.                 else
  192.                 if (num == (cache0+1))
  193.                     printf("Hit at :1\n");
  194.                 else
  195.                     printf("Hit at %lu\n", 2 + num - cache1);
  196.             }
  197.         }
  198.         else
  199.         {    if (DebugCache)
  200.             {    if (DoCache && num <= (Stuff.Info.End - 2))
  201.                     printf("Do  Cache (miss %ld %ld)\n", num-cache0, num-cache1);
  202.                 else
  203.                     printf("Non Cache (miss %ld %ld)\n", num-cache0, num-cache1);
  204.             }
  205.  
  206.             if (StatCache)
  207.                 MicSta = IBASE->Micros;
  208.  
  209.             if (DoCache && num <= (Stuff.Info.End - 2))     /* Must read 3 Blocks! */
  210.             {    cache0 = num;
  211.                 cache1 = num + 2;
  212.                 cache2 = num + Cache - 1;
  213.                 if (cache2 > Stuff.Info.End)
  214.                     cache2 = Stuff.Info.End;
  215.                 ret = DevRead(num, *buf, BLOCK_SIZE*(1 + cache2 - cache0));
  216.                 if (ret)
  217.                     cache0 = cache1 = cache2 = CACHE_INVALID;
  218.             }
  219.             else
  220.             {    if (num != Stuff.Info.End)
  221.                     cache0 = num;
  222.                 else
  223.                 {    cache0 = num - 1;
  224.                     *buf = (ulong *)&Buffer[BLOCK_SIZE];
  225.                 }
  226.  
  227.                 ret = DevRead(num, (ulong *)Buffer, BLOCK_SIZE*2);
  228.                 if (ret)
  229.                     cache0 = CACHE_INVALID;
  230.             }
  231.  
  232.             if (StatCache)
  233.                 MicFin = IBASE->Micros;
  234.  
  235.             if (StatCache)
  236.             {    if (DoCache && num <= (Stuff.Info.End - 2))
  237.                 {    ReadLarge++;
  238.                     ReadLNum += (1 + cache2 - cache0);
  239.                 }
  240.                 else
  241.                     ReadSmall++;
  242.  
  243.                 if (MicSta > MicFin)
  244.                     MicFin += 1000000;
  245.                 MicRead += (MicFin - MicSta);
  246.             }
  247.         }
  248.     }
  249.     return(ret);
  250. }
  251.  
  252. Static char *StrType(type)
  253. REG    ushort    type;
  254. {
  255.     REG    char    *ans;
  256.  
  257.     switch(type)
  258.     {
  259.     case I_ROOT:
  260.         ans = StrRoot;
  261.         break;
  262.     case I_DIR:
  263.         ans = StrDir;
  264.         break;
  265.     case I_FIL:
  266.         ans = StrFil;
  267.         break;
  268.     case I_EXT:
  269.         ans = StrExt;
  270.         break;
  271.     case I_DAT:
  272.         ans = StrDat;
  273.         break;
  274.     case I_S_LNK:
  275.         ans = StrSoftLnk;
  276.         break;
  277.     case I_H_LNK_DIR:
  278.         ans = StrDirLnk;
  279.         break;
  280.     case I_H_LNK_FIL:
  281.         ans = StrFilLnk;
  282.         break;
  283. /*
  284.     case I_UNK:
  285.     case I_UNREADABLE:
  286. */
  287.     default:
  288.         ans = StrHuh;
  289.         break;
  290.     }
  291.     return(ans);
  292. }
  293.  
  294. Static    int    BaD = 0;
  295. Static    int    WaR = 0;
  296. Static    ulong    SeqNum;
  297. Static    ulong    PrevD = 0;
  298. Static    ulong    NextD = 0;
  299.  
  300. #define B_TYP        0
  301. #define B_KEY        1
  302. #define B_CHKSUM    2
  303. #define B_BMAP        3
  304. #define B_PAR        4
  305. #define B_COUNT     5
  306. #define B_FIRST     6
  307. #define B_BYTESIZE    7
  308. #define B_HED        8
  309. #define B_RANGE     9
  310. #define B_SEQ        10
  311. #define B_SHORT     11
  312. #define B_BMFLG     12
  313. #define B_HTSIZ     13
  314. #define B_BITMAP    14
  315. #define B_NEXTD     15
  316. #define B_BADNAME    16
  317. #define B_UNR        17
  318. #define B_BLOCKSIZE    18
  319. #define B_USED        19
  320. #define B_USEDBY    20
  321.  
  322. #define B_PREVLINK    22
  323. #define B_NEXTLINK    23
  324. #define B_OFSLINK    24
  325. #define B_NOPREVLINK    25
  326. #define B_PREVINV    26
  327. #define B_NOTPREV    27
  328. #define B_PREVDIF    28
  329. #define B_LINKTYPERR    29
  330. #define B_XNOTLINK    30
  331.  
  332. Static bad(Rem, num, err, typ, ex1, ex2, ex3)
  333. struct    Info    *Rem;
  334. REG    ulong    num;
  335. int    err;
  336. REG    char    *typ;
  337. REG    ulong    ex1;
  338. ulong    ex2;
  339. ulong    ex3;
  340. {
  341.     int    idx;
  342.     ulong    blk;
  343.     REG    struct    Info    *CurRem;
  344.     REG    struct    Names    *CurName;
  345.     ulong    *Buf = (ulong *)Buffer;
  346.     int    seeked = 0;
  347.  
  348.     switch(err)
  349.     {
  350.     case B_BMFLG:
  351.     case B_BITMAP:
  352.     case B_HTSIZ:
  353.     case B_BMAP:
  354.     case B_BLOCKSIZE:
  355.     case B_BYTESIZE:
  356.     case B_OFSLINK:
  357.         WaR++;
  358.         printf("War: B %lu %s ", num, typ);
  359.         break;
  360.     default:
  361.         BaD++;
  362.         printf("Err: B %lu %s ", num, typ);
  363.         break;
  364.     }
  365.  
  366.     if (Rem && DosNames && num != Stuff.Info.Root)
  367.     {    CurRem = LastInfo;
  368.         if (Seek)
  369.             idx = CurRem - InfoList;
  370.         else
  371.             for (idx=0; CurRem != &InfoList[Stuff.Info.Root]; idx++)
  372.                 CurRem = &InfoList[CurRem->Obj.dir->parent];
  373.                     /* *** parent in same pos */
  374.  
  375.         CurName = &RootName;
  376.         while (idx--)
  377.         {    if (CurName->next == (struct Names *)NULL)
  378.             {    ZAlloc(CurName->next);
  379.                 CurName->next->prev = CurName;
  380.                 CurName->next->next = (struct Names *)NULL;
  381.                 CurName->next->block = ULONGMAX;
  382.             }
  383.             CurName = CurName->next;
  384.         }
  385.         if (CurName->next)
  386.             CurName->next->block = ULONGMAX;
  387.  
  388.         CurRem = LastInfo;
  389.         blk = LastBlk;
  390.         while (CurName)
  391.         {    if (CurName->block != blk)
  392.             {    CurName->block = blk;
  393.                 if (!Seek && !seeked)
  394.                 {    AkMotorOn(&Stuff);
  395.                     seeked = !0;
  396.                 }
  397.                 if (CacheRead(blk + Stuff.Info.Begin, &Buf))
  398.                     FName = (uchar *)"\011***Bad***";
  399.                 else
  400.                     FName = (uchar *)(&Buf[POS_NAME]);
  401.                 if (*FName >= sizeof(RootName.name))
  402.                     *FName = sizeof(RootName.name) - 1;
  403.                 Copy(CurName->name, FName, (long)((*FName) + 1), sizeof(char));
  404.             }
  405.             if (Seek)
  406.                 blk = (CurRem--)->Obj.dir->parent;
  407.                     /* *** parent in same pos, blk not used if Root */
  408.             else
  409.                 blk = InfoList[blk].Obj.dir->parent;
  410.                     /* *** parent in same pos, blk not used if Root  */
  411.  
  412.             CurName = CurName->prev;
  413.         }
  414.         if (seeked)
  415.             AkMotorOff(&Stuff);
  416.  
  417.         CurName = &RootName;
  418.         while (CurName && CurName->block != ULONGMAX)
  419.         {    if (CurName->prev)
  420.             {    if (CurName->prev == &RootName)
  421.                     putchar(':');
  422.                 else
  423.                     putchar('/');
  424.             }
  425.             if (*CurName->name)
  426.                 printf("%.*s", (int)(*(CurName->name)), (CurName->name)+1);
  427.             CurName = CurName->next;
  428.         }
  429.         putchar(' ');
  430.     }
  431.  
  432.     switch(err)
  433.     {
  434.     case B_TYP:
  435.         printf("Block type wrong (par = %lu)", ex1);
  436.         break;
  437.     case B_KEY:
  438.         printf("Key wrong (par = %lu)", ex1);
  439.         break;
  440.     case B_CHKSUM:
  441.         printf("Checksum wrong (par = %lu)", ex1);
  442.         break;
  443.     case B_BMAP:
  444.         printf("No Bitmap blocks present!");
  445.         break;
  446.     case B_PAR:
  447.         printf("Parent pointer (%lu) != parent block (%lu)", ex1, ex2);
  448.         break;
  449.     case B_COUNT:
  450.         printf("Block Count incorrect (par = %lu)", ex1);
  451.         break;
  452.     case B_FIRST:
  453.         printf("FirstData incorrect (should be = %lu)", ex1);
  454.         break;
  455.     case B_BYTESIZE:
  456.         printf("Bytesize incorrect (calc = %lu actual = %lu)", ex1, ex2);
  457.         break;
  458.     case B_HED:
  459.         printf("Header pointer (%lu) != header block (%lu)", ex1, ex2);
  460.         break;
  461.     case B_RANGE:
  462.         printf("%s pointer (%lu) out of disk range!", ex1, ex2);
  463.         break;
  464.     case B_SEQ:
  465.         printf("Sequence number wrong (should be %lu, is %lu)", ex1, ex2);
  466.         break;
  467.     case B_SHORT:
  468.         printf("Data block not full (only allowed in last data block!)");
  469.         break;
  470.     case B_BMFLG:
  471.         printf("Bitmap Flag says Bitmap is invalid!");
  472.         break;
  473.     case B_HTSIZ:
  474.         printf("Hash table size != calculated size!");
  475.         break;
  476.     case B_BITMAP:
  477.         printf("Block type NOT unknown type!");
  478.         break;
  479.     case B_NEXTD:
  480.         printf("Next data block (%lu) != Previous data block's (%lu) Next data block (%lu)",
  481.             ex1, ex2, ex3);
  482.         break;
  483.     case B_BADNAME:
  484.         printf("Blocks name is invalid (BSTR size is 0 or > %d)", sizeof(RootName.name) - 1);
  485.         break;
  486.     case B_UNR:
  487.         printf("Gave error (0x%lx) on read (par = %lu)", ex1, ex2);
  488.         break;
  489.     case B_BLOCKSIZE:
  490.         printf("Total blocks incorrect (calc = %lu actual = %lu)", ex1, ex2);
  491.         break;
  492.     case B_USED:
  493.         if (ex1)
  494.             printf("Pointed to by %lu ", ex1);
  495.         printf("Already used (FFS Error)");
  496.         break;
  497.     case B_USEDBY:
  498.         printf("Pointed to by %lu Already used by %lu (FFS Error)", ex1, ex2);
  499.         break;
  500.     case B_PREVLINK:
  501.         printf("Prev Link Pointer not 0");
  502.         break;
  503.     case B_NEXTLINK:
  504.         printf("Next Link Pointer not 0");
  505.         break;
  506.     case B_OFSLINK:
  507.         printf("Contains a link (but partition is OFS)");
  508.         break;
  509.     case B_NOPREVLINK:
  510.         printf("Prev Link Pointer should not be 0");
  511.         break;
  512.     case B_PREVINV:
  513.         printf("Prev (%lu) Must be Fil or Dir (not %s)", ex1, ex2);
  514.         break;
  515.     case B_NOTPREV:
  516.         printf("Next's (%lu) Prev (%lu) should be This", ex1, ex2);
  517.         break;
  518.     case B_PREVDIF:
  519.         printf("Next's (%lu) Prev (%lu) != This Prev (%lu)", ex3, ex2, ex1);
  520.         break;
  521.     case B_LINKTYPERR:
  522.         printf("%s Pointer's type (%s) not valid for this type", ex1, ex2);
  523.         break;
  524.     case B_XNOTLINK:
  525.         printf("%s (%lu) is not Linked", ex1, ex2);
  526.         break;
  527.     }
  528.     putchar('\n');
  529.  
  530.     if (ErrLimit >= 0 && BaD > ErrLimit)
  531.     {    printf("Validation aborted ... Error Limit (%ld) Exceeded\n", ErrLimit);
  532.         Cleanup();
  533.     }
  534.  
  535.     if (WarnLimit >= 0 && WaR > WarnLimit)
  536.     {    printf("Validation aborted ... Warning Limit (%ld) Exceeded\n", WarnLimit);
  537.         Cleanup();
  538.     }
  539. }
  540.  
  541. Static CompareLinkType(prev_is_head, prev, next)
  542. REG    int    prev_is_head;
  543. REG    ushort    prev;
  544. REG    ushort    next;
  545. {
  546.     int    ret = FALSE;
  547.  
  548.     if (!prev_is_head)
  549.     {    if (prev == I_H_LNK_DIR && next == I_H_LNK_DIR)
  550.             ret = TRUE;
  551.  
  552.         if (prev == I_H_LNK_FIL && next == I_H_LNK_FIL)
  553.             ret = TRUE;
  554.     }
  555.     else
  556.     {    if (prev == I_DIR && next == I_H_LNK_DIR)
  557.             ret = TRUE;
  558.  
  559.         if (prev == I_FIL && next == I_H_LNK_FIL)
  560.             ret = TRUE;
  561.     }
  562.  
  563.     return(ret);
  564. }
  565.  
  566. /* OK this is really quite slow - but I don't have many links! */
  567. Static CheckLinks()
  568. {
  569.     REG    struct    Links    *LinkTmp;
  570.     REG    struct    Links    *LinkTmp2;
  571.     REG    ulong    next;
  572.     REG    ulong    prev;
  573.     REG    ulong    block;
  574.     ushort    type;
  575.     int    got_prev;
  576.     int    got_next;
  577.  
  578.     LinkTmp = LinkHead;
  579.     while (LinkTmp)
  580.     {    next = LinkTmp->next;
  581.         prev = LinkTmp->prev;
  582.         block = LinkTmp->block;
  583.         type = LinkTmp->type;
  584.         got_prev = !prev;
  585.         got_next = !next;
  586.  
  587.         LinkTmp2 = LinkHead;
  588.         while (LinkTmp2)
  589.         {    if (prev && prev == LinkTmp2->block)
  590.             {    got_prev = !0;
  591.                 if (LinkTmp2->type != I_DIR && LinkTmp2->type != I_FIL)
  592.                     bad(NULL, block, B_PREVINV, StrType(type), prev, StrType(LinkTmp2->type));
  593.  
  594.                 if (!CompareLinkType(!0, LinkTmp2->type, type))
  595.                     bad(NULL, block, B_LINKTYPERR, StrType(type), StrPrev, StrType(LinkTmp2->type));
  596.             }
  597.             if (next && next == LinkTmp2->block)
  598.             {    got_next = !0;
  599.                 if (!prev && LinkTmp2->prev != block)
  600.                     bad(NULL, block, B_NOTPREV, StrType(type), next, LinkTmp2->prev);
  601.  
  602.                 if (prev && LinkTmp2->prev != prev)
  603.                     bad(NULL, block, B_PREVDIF, StrType(type), prev, LinkTmp2->prev, next);
  604.  
  605.                 if (!CompareLinkType(prev == 0, type, LinkTmp2->type))
  606.                     bad(NULL, block, B_LINKTYPERR, StrType(type), StrNext, StrType(LinkTmp2->type));
  607.             }
  608.             LinkTmp2 = LinkTmp2->another;
  609.         }
  610.         if (!got_prev)
  611.             bad(NULL, block, B_XNOTLINK, StrType(type), StrPrev, prev);
  612.         if (!got_next)
  613.             bad(NULL, block, B_XNOTLINK, StrType(type), StrNext, next);
  614.         LinkTmp = LinkTmp->another;
  615.     }
  616. }
  617.  
  618. Static MoveMaybe(block)
  619. REG    ulong    block;
  620. {
  621.     REG    struct    Links    *LinkTmpPrev = NULL;
  622.     REG    struct    Links    *LinkTmp = LinkMaybe;
  623.  
  624.     while (LinkTmp && LinkTmp->block != block)
  625.     {    LinkTmpPrev = LinkTmp;
  626.         LinkTmp = LinkTmp->another;
  627.     }
  628.  
  629.     if (LinkTmp)
  630.     {    if (LinkTmpPrev)
  631.             LinkTmpPrev->another = LinkTmp->another;
  632.         else
  633.             LinkMaybe = LinkTmp->another;
  634.  
  635.         LinkTmp->another = LinkHead;
  636.         LinkHead = LinkTmp;
  637.     }
  638.     else
  639.     {    /* Huh? Shouldn't happen? */
  640.         MyError(ERR_NOVAL, "Link Code Bug");
  641.     }
  642. }
  643.  
  644. Static AddLink(block, prev, next, type)
  645. REG    ulong    block;
  646. REG    ulong    prev;
  647. REG    ulong    next;
  648. REG    ushort    type;
  649. {
  650.     struct    Links    *LinkTmp;
  651.  
  652.     ZAlloc(LinkTmp);
  653.     LinkTmp->type = type;
  654.     LinkTmp->block = block - Stuff.Info.Begin;
  655.     LinkTmp->prev = prev;
  656.     LinkTmp->next = next;
  657.     LinkTmp->another = LinkMaybe;
  658.     LinkMaybe = LinkTmp;
  659. }
  660.  
  661. Static getblok(num, Rem)
  662. ulong    num;
  663. REG    struct    Info    *Rem;
  664. {
  665.     REG    ulong  *Buf;
  666.     REG    uchar  *obj;
  667.     REG    ulong  i;
  668.     REG    ulong  *l;
  669.     REG    ulong  *l2;
  670.     ulong    count;
  671.     ushort    *flags;
  672.     LONG    ret;
  673.     ulong    *buf;
  674.  
  675.     if (ShowReads && (++ReadCount % Count) == 0)
  676.     {    akdeco(ReadCount, 'l', NULL, NULL, NULL, ' ', ' ');
  677.         akhexo(num, 'l', NULL, NULL, NULL, '\r', '0');
  678.     }
  679.     if (Tracks && (ReadCount++ % Stuff.Info.Cyl) == 0)
  680.         akdeco(ReadCount / Stuff.Info.Cyl, 'l', NULL, NULL, NULL, '\r', ' ');
  681.  
  682.     ret = CacheRead(num, &buf);
  683.  
  684.     if (!Seek)
  685.         Rem = &InfoList[num - Stuff.Info.Begin];
  686.  
  687.     flags = &(Rem->flags);
  688.     *flags = 0;
  689.  
  690.     if (ret)
  691.     {    Rem->type = I_UNREADABLE;
  692.         Rem->Obj.ret = ret;
  693.         return;         /* N.B. */
  694.     }
  695.  
  696.     Buf = buf;
  697.  
  698.     switch(Buf[POS_TYP])
  699.     {
  700.     case TYP_CONTROL:
  701.         FName = (uchar *)(&Buf[POS_NAME]);
  702.         if (*FName == 0 || *FName >= sizeof(RootName.name))
  703.             *flags |= E_BADNAME;
  704.         switch(Buf[POS_SEC])
  705.         {
  706.         case SEC_ROOT:
  707.             Rem->type = I_ROOT;
  708.             if (Buf[POS_KEY])
  709.                 *flags |= E_KEY;
  710.  
  711. /* Appears to be a date at POS_LINK_NEXT
  712.             if (Buf[POS_LINK_PREV])
  713.                 *flags |= E_PREVLINK;
  714.             if (Buf[POS_LINK_NEXT])
  715.                 *flags |= E_NEXTLINK;
  716. */
  717.             if (!Buf[POS_BMFLG])
  718.                 *flags |= E_BMFLG;
  719.             if (Buf[POS_HTSIZ] != (1L + POS_LST_BLK - POS_FST_BLK))
  720.                 *flags |= E_HTSIZ;
  721.             if (Seek)
  722.                 (struct Root *)obj = Rem->Obj.root;
  723.             else
  724.             {    ZAlloc((struct Root *)obj);
  725.                 Rem->Obj.root = (struct Root *)obj;
  726.             }
  727.             for (count = 0, i = POS_FST_BLK, l = &Buf[i]; i <= POS_LST_BLK; l++, i++)
  728.                 if (*l)
  729.                     count++;
  730.             if (count++)
  731.             {    LAlloc(l, count, StrRoot);
  732.                 ((struct Root *)obj)->blocks = l;
  733.                 for (i = POS_FST_BLK, l2 = &Buf[i]; i <= POS_LST_BLK; l2++, i++)
  734.                     if (*l2)
  735.                         *(l++) = *l2;
  736.                 *l = 0L;
  737.             }
  738.             else
  739.                 ((struct Root *)obj)->blocks = (ulong *)NULL;
  740.  
  741.             for (i = POS_BBLK, l = &Buf[i]; *l && i < POS_BBLK_END; i++, l++)
  742.                 ;
  743.             count = i - POS_BBLK;
  744.             if (*l)
  745.                 count++;
  746.             if (count)
  747.             {    LAlloc(l, count+1, StrBitMap);
  748.                 Copy(l, &Buf[POS_BBLK], count, sizeof(ulong));
  749.                 ((struct Root *)obj)->bblocks = l;
  750.                 l[count] = 0L;
  751.             }
  752.             else
  753.                 ((struct Root *)obj)->bblocks = (ulong *)NULL;
  754.             break;
  755.         case SEC_DIR:
  756.             Rem->type = I_DIR;
  757.             if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  758.                 *flags |= E_KEY;
  759.             if (Buf[POS_LINK_PREV])
  760.                 *flags |= E_PREVLINK;
  761.             if (Buf[POS_LINK_NEXT])
  762.             {    if (FileSystem == IS_OFS && !LinksAllowed)
  763.                     *flags |= E_OFSLINK;
  764.                 AddLink(num, 0L, Buf[POS_LINK_NEXT], I_DIR);
  765.             }
  766.             if (Seek)
  767.                 (struct Dir *)obj = Rem->Obj.dir;
  768.             else
  769.             {    ZAlloc((struct Dir *)obj);
  770.                 Rem->Obj.dir = (struct Dir *)obj;
  771.             }
  772.             for (count = 0, i = POS_FST_BLK, l = &Buf[i]; i <= POS_LST_BLK; l++, i++)
  773.                 if (*l)
  774.                     count++;
  775.             if (count++)
  776.             {    LAlloc(l, count, StrDir);
  777.                 ((struct Dir *)obj)->blocks = l;
  778.                 for (i = POS_FST_BLK, l2 = &Buf[i]; i <= POS_LST_BLK; l2++, i++)
  779.                     if (*l2)
  780.                         *(l++) = *l2;
  781.                 *l = 0L;
  782.             }
  783.             else
  784.                 ((struct Dir *)obj)->blocks = (ulong *)NULL;
  785.  
  786.             ((struct Dir *)obj)->chain = Buf[POS_HASH_CH];
  787.             ((struct Dir *)obj)->parent = Buf[POS_PAR];
  788.             ((struct Dir *)obj)->next = Buf[POS_LINK_NEXT];
  789.             break;
  790.         case SEC_FIL:
  791.             Rem->type = I_FIL;
  792.             if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  793.                 *flags |= E_KEY;
  794.             if (Buf[POS_LINK_PREV])
  795.                 *flags |= E_PREVLINK;
  796.             if (Buf[POS_LINK_NEXT])
  797.             {    if (FileSystem == IS_OFS && !LinksAllowed)
  798.                     *flags |= E_OFSLINK;
  799.                 AddLink(num, 0L, Buf[POS_LINK_NEXT], I_FIL);
  800.             }
  801.             if (Seek)
  802.                 (struct Fil *)obj = Rem->Obj.fil;
  803.             else
  804.             {    ZAlloc((struct Fil *)obj);
  805.                 Rem->Obj.fil = (struct Fil *)obj;
  806.             }
  807.             for (i=POS_LST_BLK, l = &Buf[i]; *l && i > POS_FST_BLK; i--, l--)
  808.                 ;
  809.             count = POS_LST_BLK - i;
  810.             if (*l)
  811.                 count++;
  812.             else
  813.                 l++;
  814.             if (count != Buf[POS_NUM_BLK])
  815.                 *flags |= E_COUNT;
  816.             ((struct Fil *)obj)->num = count;
  817.             if (count)
  818.             {    if (Buf[POS_FST_DAT] != Buf[POS_LST_BLK])
  819.                     *flags |= E_FIRST;
  820.                 LAlloc(l2, count, StrFil);
  821.                 Copy(l2, l, count, sizeof(ulong));
  822.                 ((struct Fil *)obj)->blocks = l2;
  823.             }
  824.             else
  825.             {    ((struct Fil *)obj)->blocks = (ulong *)NULL;
  826.                 if (Buf[POS_FST_DAT])
  827.                     *flags |= E_FIRST;
  828.             }
  829.             ((struct Fil *)obj)->bytesize = Buf[POS_F_BYTSIZ];
  830.             ((struct Fil *)obj)->chain = Buf[POS_HASH_CH];
  831.             ((struct Fil *)obj)->parent = Buf[POS_PAR];
  832.             ((struct Fil *)obj)->extension = Buf[POS_EXT];
  833.             ((struct Fil *)obj)->next = Buf[POS_LINK_NEXT];
  834.             break;
  835.         case SEC_S_LNK:
  836.             Rem->type = I_S_LNK;
  837.             if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  838.                 *flags |= E_KEY;
  839.             if (Buf[POS_LINK_PREV])
  840.                 *flags |= E_PREVLINK;
  841.             if (Buf[POS_LINK_NEXT])
  842.                 *flags |= E_NEXTLINK;
  843.             if (FileSystem == IS_OFS && !LinksAllowed)
  844.                 *flags |= E_OFSLINK;
  845.             if (Seek)
  846.                 (struct Lnk *)obj = Rem->Obj.lnk;
  847.             else
  848.             {    ZAlloc((struct Lnk *)obj);
  849.                 Rem->Obj.lnk = (struct Lnk *)obj;
  850.             }
  851.             ((struct Lnk *)obj)->chain = Buf[POS_HASH_CH];
  852.             ((struct Lnk *)obj)->parent = Buf[POS_PAR];
  853.             break;
  854.         case SEC_H_LNK_DIR:
  855.             Rem->type = I_H_LNK_DIR;
  856.             if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  857.                 *flags |= E_KEY;
  858.             if (!Buf[POS_LINK_PREV])
  859.                 *flags |= E_PREVLINK;
  860.             if (FileSystem == IS_OFS && !LinksAllowed)
  861.                 *flags |= E_OFSLINK;
  862.             AddLink(num, Buf[POS_LINK_PREV], Buf[POS_LINK_NEXT], I_H_LNK_DIR);
  863.             if (Seek)
  864.                 (struct DirLnk *)obj = Rem->Obj.dirlnk;
  865.             else
  866.             {    ZAlloc((struct DirLnk *)obj);
  867.                 Rem->Obj.dirlnk = (struct DirLnk *)obj;
  868.             }
  869.             ((struct DirLnk *)obj)->prev = Buf[POS_LINK_PREV];
  870.             ((struct DirLnk *)obj)->next = Buf[POS_LINK_NEXT];
  871.             ((struct DirLnk *)obj)->chain = Buf[POS_HASH_CH];
  872.             ((struct DirLnk *)obj)->parent = Buf[POS_PAR];
  873.             break;
  874.         case SEC_H_LNK_FIL:
  875.             Rem->type = I_H_LNK_FIL;
  876.             if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  877.                 *flags |= E_KEY;
  878.             if (!Buf[POS_LINK_PREV])
  879.                 *flags |= E_PREVLINK;
  880.             if (FileSystem == IS_OFS && !LinksAllowed)
  881.                 *flags |= E_OFSLINK;
  882.             AddLink(num, Buf[POS_LINK_PREV], Buf[POS_LINK_NEXT], I_H_LNK_FIL);
  883.             if (Seek)
  884.                 (struct FilLnk *)obj = Rem->Obj.fillnk;
  885.             else
  886.             {    ZAlloc((struct FilLnk *)obj);
  887.                 Rem->Obj.fillnk = (struct FilLnk *)obj;
  888.             }
  889.             ((struct FilLnk *)obj)->prev = Buf[POS_LINK_PREV];
  890.             ((struct FilLnk *)obj)->next = Buf[POS_LINK_NEXT];
  891.             ((struct FilLnk *)obj)->chain = Buf[POS_HASH_CH];
  892.             ((struct FilLnk *)obj)->parent = Buf[POS_PAR];
  893.             break;
  894.         default:
  895.             Rem->type = I_UNK;
  896.             break;
  897.         }
  898.         break;
  899.     case TYP_EXT:
  900.         Rem->type = I_EXT;
  901.         if (Buf[POS_KEY] != (num - Stuff.Info.Begin))
  902.             *flags |= E_KEY;
  903.         if (Seek)
  904.             (struct Ext *)obj = Rem->Obj.ext;
  905.         else
  906.         {    ZAlloc((struct Ext *)obj);
  907.             Rem->Obj.ext = (struct Ext *)obj;
  908.         }
  909.         if (Buf[POS_FST_DAT])
  910.             *flags |= E_FIRST;
  911.         for (i=POS_LST_BLK, l = &Buf[i]; *l && i > POS_FST_BLK; i--, l--)
  912.             ;
  913.         count = POS_LST_BLK - i;
  914.         if (*l)
  915.             count++;
  916.         else
  917.             l++;
  918.         if (count != Buf[POS_NUM_BLK])
  919.             *flags |= E_COUNT;
  920.         ((struct Ext *)obj)->num = count;
  921.         if (count)
  922.         {    LAlloc(l2, count, StrExt);
  923.             Copy(l2, l, count, sizeof(ulong));
  924.             ((struct Ext *)obj)->blocks = l2;
  925.         }
  926.         else
  927.             ((struct Ext *)obj)->blocks = (ulong *)NULL;
  928.         ((struct Ext *)obj)->parent = Buf[POS_PAR];
  929.         ((struct Ext *)obj)->extension = Buf[POS_EXT];
  930.         break;
  931.     case TYP_DAT:
  932.         Rem->type = I_DAT;
  933.         if (Seek)
  934.             (struct Dat *)obj = Rem->Obj.dat;
  935.         else
  936.         {    ZAlloc((struct Dat *)obj);
  937.             Rem->Obj.dat = (struct Dat *)obj;
  938.         }
  939.         ((struct Dat *)obj)->key = Buf[POS_KEY];
  940.         ((struct Dat *)obj)->seqnum = Buf[POS_D_SEQ_NUM];
  941.         ((struct Dat *)obj)->size = Buf[POS_D_DAT_SIZ];
  942.         ((struct Dat *)obj)->nextd = Buf[POS_D_NXT_DAT];
  943.         if (((struct Dat *)obj)->nextd)
  944.             if (((struct Dat *)obj)->size != (BLOCK_SIZE - (POS_FST_BLK << 2L)))
  945.                 *flags |= E_SHORT;
  946.         break;
  947.     default:
  948.         Rem->type = I_UNK;
  949.         break;
  950.     }
  951.     if (Rem->type != I_UNK && (Rem->type != I_DAT || FileSystem == IS_OFS))
  952.         if (DosChecksum(Buf))
  953.             *flags |= E_CHKSUM;
  954. }
  955.  
  956. Static rchk(Rem, num, blk, typ, want)
  957. REG    struct    Info    *Rem;
  958. REG    ulong    num;
  959. REG    ulong    blk;
  960. REG    char    *typ;
  961. REG    char    *want;
  962. {
  963.     if (blk < Stuff.Info.res || blk > (Stuff.Info.End - Stuff.Info.Begin))
  964.     {    bad(Rem, num, B_RANGE, typ, want, blk);
  965.         return(0);
  966.     }
  967.     return(!0);
  968. }
  969.  
  970. Static int unreadable(Rem, num, Str, par)
  971. REG    struct    Info    *Rem;
  972. REG    ulong    num;
  973. REG    char    *Str;
  974. REG    ulong    par;
  975. {
  976.     if (Rem->type == I_UNREADABLE)
  977.     {    bad(Rem, num, B_UNR, Str, Rem->Obj.ret, par);
  978.         return(!0);
  979.     }
  980.     else
  981.         return(0);
  982. }
  983.  
  984. Static FreeSeek(Rem)
  985. REG    struct    Info    *Rem;
  986. {
  987.     REG    ulong    *l;
  988.     REG    ulong    count;
  989.  
  990.     if (Seek)
  991.         switch(Rem->type)
  992.         {
  993.         case I_ROOT:
  994.             l = Rem->Obj.root->bblocks;
  995.             if (l)
  996.             {    for (count = 1; *l; l++, count++)
  997.                     ;
  998.                 FreeMem(Rem->Obj.root->bblocks, ULONGS(count));
  999.             }
  1000.             l = Rem->Obj.root->blocks;
  1001.             if (l)
  1002.             {    for (count = 1; *l; l++, count++)
  1003.                     ;
  1004.                 FreeMem(Rem->Obj.root->blocks, ULONGS(count));
  1005.             }
  1006.             break;
  1007.         case I_DIR:
  1008.             l = Rem->Obj.dir->blocks;
  1009.             if (l)
  1010.             {    for (count = 1; *l; l++, count++)
  1011.                     ;
  1012.                 FreeMem(Rem->Obj.dir->blocks, ULONGS(count));
  1013.             }
  1014.             break;
  1015.         case I_FIL:
  1016.             if (Rem->Obj.fil->blocks)
  1017.                 FreeMem(Rem->Obj.fil->blocks, ULONGS(Rem->Obj.fil->num));
  1018.             break;
  1019.         case I_EXT:
  1020.             if (Rem->Obj.ext->blocks)
  1021.                 FreeMem(Rem->Obj.ext->blocks, ULONGS(Rem->Obj.ext->num));
  1022.             break;
  1023. /*
  1024.         case I_DAT:
  1025.         case I_S_LNK:
  1026.         case I_H_LNK_DIR:
  1027.         case I_H_LNK_FIL:
  1028. */
  1029.         default:
  1030.             break;
  1031.         }
  1032. }
  1033.  
  1034. Static CheckUsed(num, Rem, str, par)
  1035. REG    ulong    num;
  1036. REG    struct    Info    *Rem;
  1037. REG    char    *str;
  1038. REG    ulong    par;
  1039. {
  1040.     if (FileSystem == IS_FFS)
  1041.         if (Seek)
  1042.         {    if (BMAP_TST(num))
  1043.                 bad(Rem, num, B_USED, str, par);
  1044.             else
  1045.                 BMAP_SET(num);
  1046.         }
  1047.         else
  1048.         {    if (Rem->flags & I_USED)
  1049.                 switch(Rem->type)
  1050.                 {
  1051.                 case I_DAT:    /* well maybe? ;-) */
  1052.                     bad(Rem, num, B_USEDBY, str, par, Rem->Obj.dat->key);
  1053.                     break;
  1054.                 case I_DIR:
  1055.                 case I_FIL:
  1056.                 case I_EXT:
  1057.                 case I_S_LNK:
  1058.                 case I_H_LNK_DIR:
  1059.                 case I_H_LNK_FIL:
  1060.                     bad(Rem, num, B_USEDBY, str, par, Rem->Obj.dir->parent);
  1061.                     break;
  1062. /* Usually FFS Data Block
  1063.                 case I_ROOT:
  1064.                 case I_UNK:
  1065.                 case I_UNREADABLE:
  1066. */
  1067.                 default:
  1068.                     bad(Rem, num, B_USED, str, par);
  1069.                     break;
  1070.                 }
  1071.             else
  1072.                 Rem->flags |= I_USED;
  1073.         }
  1074. }
  1075.  
  1076. Static long ChkDat(num, hed, Rem, key)
  1077. REG    ulong    num;
  1078. REG    ulong    hed;
  1079. REG    struct    Info    *Rem;
  1080. REG    ulong    key;
  1081. {
  1082.     REG    ulong  *l;
  1083.  
  1084.     DoCache = !0;
  1085.  
  1086.     if (Seek)
  1087.         Get(Rem, num);
  1088.     else
  1089.         Rem = &InfoList[num];
  1090.  
  1091.     CheckUsed(num, Rem, StrDat, key);
  1092.  
  1093.     if (unreadable(Rem, num, StrDat, hed))
  1094.         return(0);
  1095.  
  1096.     if (FileSystem == IS_FFS)
  1097.     {    FreeSeek(Rem);
  1098.         return(1);
  1099.     }
  1100.  
  1101.     PrevD = num;
  1102.     NextD = ULONGMAX;
  1103.     SeqNum++;
  1104.  
  1105.     if (Rem->type != I_DAT)
  1106.     {    bad(Rem, num, B_TYP, StrDat, hed);
  1107.  
  1108.         if (Rem->flags & E_CHKSUM)
  1109.             bad(Rem, num, B_CHKSUM, StrDat, hed);
  1110.  
  1111.         FreeSeek(Rem);
  1112.         return(0);
  1113.     }
  1114.     else
  1115.     {    if (Rem->Obj.dat->key != hed)
  1116.             bad(Rem, num, B_HED, StrDat, Rem->Obj.dat->key, hed);
  1117.         if (Rem->flags & E_CHKSUM)
  1118.             bad(Rem, num, B_CHKSUM, StrDat, hed);
  1119.  
  1120.         if (Rem->Obj.dat->seqnum != SeqNum)
  1121.             bad(Rem, num, B_SEQ, StrDat, SeqNum, Rem->Obj.dat->seqnum);
  1122.  
  1123.         if (Rem->flags & E_SHORT)
  1124.             bad(Rem, num, B_SHORT, StrDat);
  1125.  
  1126.         NextD = Rem->Obj.dat->nextd;
  1127.         if (NextD)
  1128.             rchk(Rem, num, NextD, StrDat, StrNextD);
  1129.  
  1130.         FreeSeek(Rem);
  1131.         return(Rem->Obj.dat->size);
  1132.     }
  1133. }
  1134.  
  1135. Static long ChkExt(num, par, Rem)
  1136. ulong    num;
  1137. ulong    par;
  1138. REG    struct    Info    *Rem;
  1139. {
  1140.     REG    ulong  *l;
  1141.     REG    ulong  *l2;
  1142.     REG    ulong  bytesize = 0;
  1143.     REG    int    oBaD = BaD;
  1144.  
  1145.     DoCache = 0;
  1146.  
  1147.     if (Seek)
  1148.         Get(Rem, num);
  1149.     else
  1150.         Rem = &InfoList[num];
  1151.  
  1152.     CheckUsed(num, Rem, StrExt, par);
  1153.  
  1154.     if (unreadable(Rem, num, StrExt, par))
  1155.         return(0);
  1156.  
  1157.     if (Rem->type != I_EXT)
  1158.         bad(Rem, num, B_TYP, StrExt, par);
  1159.     else
  1160.         if (Rem->Obj.ext->parent != par)
  1161.             bad(Rem, num, B_PAR, StrExt, Rem->Obj.ext->parent, par);
  1162.  
  1163.     if (Rem->flags & E_KEY)
  1164.         bad(Rem, num, B_KEY, StrExt, par);
  1165.     if (Rem->flags & E_CHKSUM)
  1166.         bad(Rem, num, B_CHKSUM, StrExt, par);
  1167.  
  1168.     if (Rem->flags & E_COUNT)
  1169.         bad(Rem, num, B_COUNT, StrExt, par);
  1170.     if (Rem->flags & E_FIRST)
  1171.         bad(Rem, num, B_FIRST, StrExt, 0L);
  1172.  
  1173.     if (IgnoreErr || BaD == oBaD)
  1174.     {    l = Rem->Obj.ext->blocks;
  1175.         if (l)
  1176.         {    for (l2 = &l[Rem->Obj.ext->num - 1]; l2 >= l; l2--)
  1177.                 if (rchk(Rem, num, *l2, StrExt, StrDat))
  1178.                 {    if (FileSystem == IS_OFS && NextD != *l2 && NextD != ULONGMAX)
  1179.                         bad(Rem, num, B_NEXTD, StrExt, *l2, PrevD, NextD);
  1180.                     bytesize += ChkDat(*l2, par, Rem+1, num);
  1181.                 }
  1182.             FreeSeek(Rem);
  1183.             if (Rem->Obj.ext->extension && rchk(Rem, num, Rem->Obj.ext->extension, StrExt, StrExt))
  1184.                 bytesize += ChkExt(Rem->Obj.ext->extension, par, Rem);
  1185.                 /* Same Level (and don't need Rem anymore) */
  1186.         }
  1187.         else
  1188.             FreeSeek(Rem);
  1189.     }
  1190.     else
  1191.         FreeSeek(Rem);
  1192.  
  1193.     return(bytesize);
  1194. }
  1195.  
  1196. Static ChkFil(num, par, Rem)
  1197. ulong    num;
  1198. ulong    par;
  1199. REG    struct    Info    *Rem;
  1200. {
  1201.     REG    ulong  *l;
  1202.     REG    ulong  *l2;
  1203.     REG    ulong  bytesize = 0;
  1204.     REG    int    oBaD = BaD;
  1205.     int    flag = 0;
  1206.     struct    Info    *oLastInfo = LastInfo;
  1207.     ulong    oLastBlk = LastBlk;
  1208.  
  1209.     LastInfo = Rem;
  1210.     LastBlk = num;
  1211.  
  1212.     if (Rem->flags & E_KEY)
  1213.         bad(Rem, num, B_KEY, StrFil, par);
  1214.     if (Rem->flags & E_CHKSUM)
  1215.         bad(Rem, num, B_CHKSUM, StrFil, par);
  1216.     if (Rem->flags & E_COUNT)
  1217.         bad(Rem, num, B_COUNT, StrFil, par);
  1218.     if (Rem->flags & E_FIRST)
  1219.     {    if (Rem->Obj.fil->blocks)
  1220.             bad(Rem, num, B_FIRST, StrFil, Rem->Obj.fil->blocks[Rem->Obj.fil->num - 1]);
  1221.         else
  1222.             bad(Rem, num, B_FIRST, StrFil, 0L);
  1223.     }
  1224.     if (Rem->flags & E_BADNAME)
  1225.         bad(Rem, num, B_BADNAME, StrFil);
  1226.     if (Rem->flags & E_PREVLINK)
  1227.         bad(Rem, num, B_PREVLINK, StrFil);
  1228.     if (Rem->flags & E_OFSLINK)
  1229.         bad(Rem, num, B_OFSLINK, StrFil);
  1230.  
  1231.     if (Rem->Obj.fil->parent != par)
  1232.         bad(Rem, num, B_PAR, StrFil, Rem->Obj.fil->parent, par);
  1233.  
  1234.     if (Rem->Obj.fil->next)
  1235.     {    rchk(Rem, num, Rem->Obj.fil->next, StrFil, StrNext);
  1236.         MoveMaybe(num);
  1237.     }
  1238.  
  1239.     if (IgnoreErr || BaD == oBaD)
  1240.     {    SeqNum = 0L;
  1241.         l = Rem->Obj.fil->blocks;
  1242.         if (l)
  1243.         {    for(l2 = &l[Rem->Obj.fil->num - 1]; l2 >= l; l2--)
  1244.                 if (rchk(Rem, num, *l2, StrFil, StrDat))
  1245.                 {    if (flag)
  1246.                     {    if (FileSystem == IS_OFS && NextD != *l2 && NextD != ULONGMAX)
  1247.                             bad(Rem, num, B_NEXTD, StrFil, *l2, PrevD, NextD);
  1248.                     }
  1249.                     else
  1250.                         flag = !0;
  1251.                     bytesize += ChkDat(*l2, num, Rem+1, num);
  1252.                 }
  1253.             if (Rem->Obj.fil->extension && rchk(Rem, num, Rem->Obj.fil->extension, StrFil, StrExt))
  1254.                 bytesize += ChkExt(Rem->Obj.fil->extension, num, Rem+1);
  1255.             if (FileSystem == IS_OFS && NextD && NextD != ULONGMAX)
  1256.                 bad(Rem, num, B_NEXTD, StrFil, 0L, PrevD, NextD);
  1257.  
  1258.             if (FileSystem == IS_OFS)
  1259.                 if (bytesize != Rem->Obj.fil->bytesize)
  1260.                     bad(Rem, num, B_BYTESIZE, StrFil, bytesize, Rem->Obj.fil->bytesize);
  1261.  
  1262.             if (FileSystem == IS_FFS)
  1263.             {    Rem->Obj.fil->bytesize += 511;
  1264.                 Rem->Obj.fil->bytesize /= 512;    /* Calculate number of blocks - any other check available ???? */
  1265.                 if (bytesize != Rem->Obj.fil->bytesize)
  1266.                     bad(Rem, num, B_BLOCKSIZE, StrFil, bytesize, Rem->Obj.fil->bytesize);
  1267.             }
  1268.         }
  1269.         LastInfo = oLastInfo;
  1270.         LastBlk = oLastBlk;
  1271.  
  1272.         FreeSeek(Rem);
  1273.  
  1274.         if (Rem->Obj.fil->chain && rchk(Rem, num, Rem->Obj.fil->chain, StrFil, StrChain))
  1275.             ChkEtc(Rem->Obj.fil->chain, par, Rem);
  1276.             /* Same Level (and don't need Rem anymore) */
  1277.     }
  1278.     else
  1279.     {    LastInfo = oLastInfo;
  1280.         LastBlk = oLastBlk;
  1281.  
  1282.         FreeSeek(Rem);
  1283.     }
  1284. }
  1285.  
  1286. Static ChkDir(num, par, Rem)
  1287. REG    ulong    num;
  1288. REG    ulong    par;
  1289. REG    struct    Info    *Rem;
  1290. {
  1291.     REG    ulong  *l;
  1292.     REG    int    oBaD = BaD;
  1293.     struct    Info    *oLastInfo = LastInfo;
  1294.     ulong    oLastBlk = LastBlk;
  1295.  
  1296.     LastInfo = Rem;
  1297.     LastBlk = num;
  1298.  
  1299.     if (Rem->flags & E_KEY)
  1300.         bad(Rem, num, B_KEY, StrDir, par);
  1301.     if (Rem->flags & E_CHKSUM)
  1302.         bad(Rem, num, B_CHKSUM, StrDir, par);
  1303.     if (Rem->flags & E_BADNAME)
  1304.         bad(Rem, num, B_BADNAME, StrDir);
  1305.     if (Rem->flags & E_PREVLINK)
  1306.         bad(Rem, num, B_PREVLINK, StrDir);
  1307.     if (Rem->flags & E_OFSLINK)
  1308.         bad(Rem, num, B_OFSLINK, StrDir);
  1309.  
  1310.     if (Rem->Obj.dir->parent != par)
  1311.         bad(Rem, num, B_PAR, StrDir, Rem->Obj.dir->parent, par);
  1312.  
  1313.     if (Rem->Obj.dir->next)
  1314.     {    rchk(Rem, num, Rem->Obj.dir->next, StrDir, StrNext);
  1315.         MoveMaybe(num);
  1316.     }
  1317.  
  1318.     if (IgnoreErr || BaD == oBaD)
  1319.     {    l = Rem->Obj.dir->blocks;
  1320.         if (l)
  1321.             for (; *l; l++)
  1322.                 if (rchk(Rem, num, *l, StrDir, StrEtc))
  1323.                     ChkEtc(*l, num, Rem+1);
  1324.  
  1325.         LastInfo = oLastInfo;
  1326.         LastBlk = oLastBlk;
  1327.  
  1328.         FreeSeek(Rem);
  1329.  
  1330.         if (Rem->Obj.dir->chain && rchk(Rem, num, Rem->Obj.dir->chain, StrDir, StrChain))
  1331.             ChkEtc(Rem->Obj.dir->chain, par, Rem);
  1332.             /* Same Level (and don't need Rem anymore) */
  1333.     }
  1334.     else
  1335.     {    LastInfo = oLastInfo;
  1336.         LastBlk = oLastBlk;
  1337.  
  1338.         FreeSeek(Rem);
  1339.     }
  1340. }
  1341.  
  1342. Static ChkLnk(num, par, Rem)
  1343. REG    ulong    num;
  1344. REG    ulong    par;
  1345. REG    struct    Info    *Rem;
  1346. {
  1347.     REG    ulong  *l;
  1348.     REG    int    oBaD = BaD;
  1349.     struct    Info    *oLastInfo = LastInfo;
  1350.     ulong    oLastBlk = LastBlk;
  1351.  
  1352.     LastInfo = Rem;
  1353.     LastBlk = num;
  1354.  
  1355.     if (Rem->flags & E_KEY)
  1356.         bad(Rem, num, B_KEY, StrSoftLnk, par);
  1357.     if (Rem->flags & E_CHKSUM)
  1358.         bad(Rem, num, B_CHKSUM, StrSoftLnk, par);
  1359.     if (Rem->flags & E_BADNAME)
  1360.         bad(Rem, num, B_BADNAME, StrSoftLnk);
  1361.     if (Rem->flags & E_PREVLINK)
  1362.         bad(Rem, num, B_PREVLINK, StrRoot);
  1363.     if (Rem->flags & E_NEXTLINK)
  1364.         bad(Rem, num, B_NEXTLINK, StrRoot);
  1365.     if (Rem->flags & E_OFSLINK)
  1366.         bad(Rem, num, B_OFSLINK, StrSoftLnk);
  1367.  
  1368.     if (Rem->Obj.lnk->parent != par)
  1369.         bad(Rem, num, B_PAR, StrSoftLnk, Rem->Obj.lnk->parent, par);
  1370.  
  1371.     LastInfo = oLastInfo;
  1372.     LastBlk = oLastBlk;
  1373.  
  1374.     FreeSeek(Rem);
  1375.  
  1376.     if (IgnoreErr || BaD == oBaD)
  1377.     {    if (Rem->Obj.lnk->chain && rchk(Rem, num, Rem->Obj.lnk->chain, StrSoftLnk, StrChain))
  1378.             ChkEtc(Rem->Obj.lnk->chain, par, Rem);
  1379.             /* Same Level (and don't need Rem anymore) */
  1380.     }
  1381. }
  1382.  
  1383. Static ChkDirLnk(num, par, Rem)
  1384. REG    ulong    num;
  1385. REG    ulong    par;
  1386. REG    struct    Info    *Rem;
  1387. {
  1388.     REG    ulong  *l;
  1389.     REG    int    oBaD = BaD;
  1390.     struct    Info    *oLastInfo = LastInfo;
  1391.     ulong    oLastBlk = LastBlk;
  1392.  
  1393.     LastInfo = Rem;
  1394.     LastBlk = num;
  1395.  
  1396.     if (Rem->flags & E_KEY)
  1397.         bad(Rem, num, B_KEY, StrDirLnk, par);
  1398.     if (Rem->flags & E_CHKSUM)
  1399.         bad(Rem, num, B_CHKSUM, StrDirLnk, par);
  1400.     if (Rem->flags & E_BADNAME)
  1401.         bad(Rem, num, B_BADNAME, StrDirLnk);
  1402.     if (Rem->flags & E_PREVLINK)
  1403.         bad(Rem, num, B_NOPREVLINK, StrDirLnk);
  1404.     if (Rem->flags & E_OFSLINK)
  1405.         bad(Rem, num, B_OFSLINK, StrDirLnk);
  1406.  
  1407.     if (Rem->Obj.dirlnk->parent != par)
  1408.         bad(Rem, num, B_PAR, StrDirLnk, Rem->Obj.dirlnk->parent, par);
  1409.  
  1410.     if (Rem->Obj.dirlnk->prev)
  1411.         rchk(Rem, num, Rem->Obj.dirlnk->prev, StrDirLnk, StrPrev);
  1412.     if (Rem->Obj.dirlnk->next)
  1413.         rchk(Rem, num, Rem->Obj.dirlnk->next, StrDirLnk, StrNext);
  1414.  
  1415.     MoveMaybe(num);
  1416.  
  1417.     LastInfo = oLastInfo;
  1418.     LastBlk = oLastBlk;
  1419.  
  1420.     FreeSeek(Rem);
  1421.  
  1422.     if (IgnoreErr || BaD == oBaD)
  1423.     {    if (Rem->Obj.dirlnk->chain && rchk(Rem, num, Rem->Obj.dirlnk->chain, StrDirLnk, StrChain))
  1424.             ChkEtc(Rem->Obj.dirlnk->chain, par, Rem);
  1425.             /* Same Level (and don't need Rem anymore) */
  1426.     }
  1427. }
  1428.  
  1429. Static ChkFilLnk(num, par, Rem)
  1430. REG    ulong    num;
  1431. REG    ulong    par;
  1432. REG    struct    Info    *Rem;
  1433. {
  1434.     REG    ulong  *l;
  1435.     REG    int    oBaD = BaD;
  1436.     struct    Info    *oLastInfo = LastInfo;
  1437.     ulong    oLastBlk = LastBlk;
  1438.  
  1439.     LastInfo = Rem;
  1440.     LastBlk = num;
  1441.  
  1442.     if (Rem->flags & E_KEY)
  1443.         bad(Rem, num, B_KEY, StrFilLnk, par);
  1444.     if (Rem->flags & E_CHKSUM)
  1445.         bad(Rem, num, B_CHKSUM, StrFilLnk, par);
  1446.     if (Rem->flags & E_BADNAME)
  1447.         bad(Rem, num, B_BADNAME, StrFilLnk);
  1448.     if (Rem->flags & E_PREVLINK)
  1449.         bad(Rem, num, B_NOPREVLINK, StrFilLnk);
  1450.     if (Rem->flags & E_OFSLINK)
  1451.         bad(Rem, num, B_OFSLINK, StrFilLnk);
  1452.  
  1453.     if (Rem->Obj.fillnk->parent != par)
  1454.         bad(Rem, num, B_PAR, StrFilLnk, Rem->Obj.fillnk->parent, par);
  1455.  
  1456.     if (Rem->Obj.fillnk->prev)
  1457.         rchk(Rem, num, Rem->Obj.fillnk->prev, StrFilLnk, StrPrev);
  1458.     if (Rem->Obj.fillnk->next)
  1459.         rchk(Rem, num, Rem->Obj.fillnk->next, StrFilLnk, StrNext);
  1460.  
  1461.     MoveMaybe(num);
  1462.  
  1463.     LastInfo = oLastInfo;
  1464.     LastBlk = oLastBlk;
  1465.  
  1466.     FreeSeek(Rem);
  1467.  
  1468.     if (IgnoreErr || BaD == oBaD)
  1469.     {    if (Rem->Obj.fillnk->chain && rchk(Rem, num, Rem->Obj.fillnk->chain, StrFilLnk, StrChain))
  1470.             ChkEtc(Rem->Obj.fillnk->chain, par, Rem);
  1471.             /* Same Level (and don't need Rem anymore) */
  1472.     }
  1473. }
  1474.  
  1475. Static ChkEtc(num, par, Rem)
  1476. REG    ulong    num;
  1477. REG    ulong    par;
  1478. REG    struct    Info    *Rem;
  1479. {
  1480.     REG    ulong  *l;
  1481.  
  1482.     DoCache = 0;
  1483.  
  1484.     if (Seek)
  1485.         Get(Rem, num);
  1486.     else
  1487.         Rem = &InfoList[num];
  1488.  
  1489.     CheckUsed(num, Rem, StrEtc, par);
  1490.  
  1491.     if (unreadable(Rem, num, StrEtc, par))
  1492.         return;
  1493.  
  1494.     switch(Rem->type)
  1495.     {
  1496.     case I_DIR:
  1497.         ChkDir(num, par, Rem);
  1498.         break;
  1499.     case I_FIL:
  1500.         ChkFil(num, par, Rem);
  1501.         break;
  1502.     case I_S_LNK:
  1503.         ChkLnk(num, par, Rem);
  1504.         break;
  1505.     case I_H_LNK_DIR:
  1506.         ChkDirLnk(num, par, Rem);
  1507.         break;
  1508.     case I_H_LNK_FIL:
  1509.         ChkFilLnk(num, par, Rem);
  1510.         break;
  1511.     default:
  1512.         bad(Rem, num, B_TYP, StrEtc, par);
  1513.         FreeSeek(Rem);
  1514.         break;
  1515.     }
  1516. }
  1517.  
  1518. Static ChkBMap(num, par, Rem)
  1519. REG    ulong    num;
  1520. REG    ulong    par;
  1521. REG    struct    Info    *Rem;
  1522. {
  1523.     DoCache = !0;
  1524.  
  1525.     if (Seek)
  1526.         Get(Rem, num);
  1527.     else
  1528.         Rem = &InfoList[num];
  1529.  
  1530.     CheckUsed(num, Rem, StrBitMap, par);
  1531.  
  1532.     if (unreadable(Rem, num, StrBitMap, par))
  1533.         return;
  1534.  
  1535.     if (Rem->flags & E_CHKSUM)
  1536.         bad(Rem, num, B_CHKSUM, StrBitMap, par);
  1537.  
  1538.     if (Rem->type != I_UNK)
  1539.         bad(Rem, num, B_BITMAP, StrBitMap);
  1540. /* How do I test that this really is a bitmap block? Probably generate a warning */
  1541.  
  1542.     FreeSeek(Rem);
  1543. }
  1544.  
  1545. Static ChkRoot(num, Rem)
  1546. REG    ulong    num;
  1547. REG    struct    Info    *Rem;
  1548. {
  1549.     REG    ulong  *l;
  1550.     REG    int    oBaD = BaD;
  1551.  
  1552.     if (Seek)
  1553.         Get(Rem, num);
  1554.     else
  1555.         Rem = &InfoList[num];
  1556.  
  1557.     LastInfo = Rem;
  1558.     LastBlk = num;
  1559.  
  1560.     CheckUsed(num, Rem, StrRoot, 0L);
  1561.  
  1562.     if (unreadable(Rem, num, StrRoot, 0L))
  1563.         return;
  1564.  
  1565.     if (Rem->type != I_ROOT)
  1566.         bad(Rem, num, B_TYP, StrRoot, 0L);
  1567.     if (Rem->flags & E_BMFLG)
  1568.         bad(Rem, num, B_BMFLG, StrRoot);
  1569.     if (Rem->flags & E_HTSIZ)
  1570.         bad(Rem, num, B_HTSIZ, StrRoot);
  1571.     if (Rem->flags & E_KEY)
  1572.         bad(Rem, num, B_KEY, StrRoot, 0L);
  1573.     if (Rem->flags & E_CHKSUM)
  1574.         bad(Rem, num, B_CHKSUM, StrRoot, 0L);
  1575.     if (Rem->flags & E_BADNAME)
  1576.         bad(Rem, num, B_BADNAME, StrRoot);
  1577.  
  1578. /* appears to be a date in NEXTLINK position
  1579.     if (Rem->flags & E_PREVLINK)
  1580.         bad(Rem, num, B_PREVLINK, StrRoot);
  1581.     if (Rem->flags & E_NEXTLINK)
  1582.         bad(Rem, num, B_NEXTLINK, StrRoot);
  1583. */
  1584.  
  1585.     if (IgnoreErr || BaD == oBaD)
  1586.     {    l = Rem->Obj.root->bblocks;
  1587.         if (!l)
  1588.             bad(Rem, num, B_BMAP, NULL);
  1589.         else
  1590.             for (; *l; l++)
  1591.                 if (rchk(Rem, num, *l, StrRoot, StrBitMap))
  1592.                     ChkBMap(*l, num, Rem+1);
  1593.  
  1594.         l = Rem->Obj.root->blocks;
  1595.         if (l)
  1596.             for (; *l; l++)
  1597.                 if (rchk(Rem, num, *l, StrRoot, StrEtc))
  1598.                     ChkEtc(*l, num, Rem+1);
  1599.     }
  1600.     FreeSeek(Rem);
  1601. }
  1602.  
  1603. Static    ValidateIt()
  1604. {
  1605.     REG    ulong    l;
  1606.     REG    ulong    size;
  1607.     REG    ulong    *bptr;
  1608.  
  1609.     if (Seek)
  1610.         size = MaxDepth;
  1611.     else
  1612.         size = 1L + Stuff.Info.End - Stuff.Info.Begin;
  1613.  
  1614.     MPAlloc(InfoList, (sizeof(*InfoList) * size), &GenPoolKey);
  1615.  
  1616.     if (!Seek)
  1617.     {    AkMotorOn(&Stuff);
  1618.         DoCache = !0;
  1619.         for (l = Stuff.Info.Begin; l <= Stuff.Info.End; l++)
  1620.             getblok(l, NULL);
  1621.         AkMotorOff(&Stuff);
  1622.         ChkRoot(Stuff.Info.Root, NULL);
  1623.     }
  1624.     else
  1625.     {    while (size-- > 0)
  1626.             MPAlloc(InfoList[size].Obj.fil, sizeof(struct Fil), &GenPoolKey);
  1627.         if (FileSystem == IS_FFS)
  1628.         {    size = 1L + Stuff.Info.End - Stuff.Info.Begin;
  1629.             MPAlloc(MyBitMap, (size+7)/8, &ZerPoolKey);
  1630.         }
  1631.         AkMotorOn(&Stuff);
  1632.         DoCache = !0;
  1633.         ChkRoot(Stuff.Info.Root, InfoList);
  1634.         AkMotorOff(&Stuff);
  1635.     }
  1636.  
  1637.     CheckLinks();
  1638.  
  1639.     if (BaD || WaR)
  1640.         printf("\nTotal Displayed errors for %s %d, Warnings: %d\n", Device, BaD, WaR);
  1641.     else
  1642.         printf("\nNo errors detected on %s\n", Device);
  1643.  
  1644.     if (DebugLink)
  1645.     {    puts("Links:");
  1646.         if (!LinkHead)
  1647.             puts("\tNone!");
  1648.         else
  1649.         {    while (LinkHead)
  1650.             {    printf("\t%-10s Blk:%10lu Prev:%10lu Next:%10lu\n",
  1651.                     StrType(LinkHead->type),
  1652.                     LinkHead->block,
  1653.                     LinkHead->prev,
  1654.                     LinkHead->next);
  1655.                 LinkHead = LinkHead->another;
  1656.             }
  1657.         }
  1658.         puts("Maybes: (but not!)");
  1659.         if (!LinkMaybe)
  1660.             puts("\tNone!");
  1661.         else
  1662.         {    while (LinkMaybe)
  1663.             {    printf("\t%-10s Blk:%10lu Prev:%10lu Next:%10lu\n",
  1664.                     StrType(LinkMaybe->type),
  1665.                     LinkMaybe->block,
  1666.                     LinkMaybe->prev,
  1667.                     LinkMaybe->next);
  1668.                 LinkMaybe = LinkMaybe->another;
  1669.             }
  1670.         }
  1671.     }
  1672. }
  1673.  
  1674. Static    Val()
  1675. {
  1676.     LONG    ret;
  1677.     ulong    *buf;
  1678.  
  1679.     if (Dev = AkDevOpen(Device, &Stuff))
  1680.         MyError(Dev, Device);
  1681.  
  1682.     if (Inhibit)
  1683.         Inhibited = AkDevInhibit(&Stuff, -1L);
  1684.  
  1685.     MPAlloc(Buffer, (long)(BLOCK_SIZE*Cache), &BufPoolKey);
  1686.  
  1687.     AkDevMsg(&(Stuff.Info));
  1688.  
  1689.     if (Ffs || Ofs)         /* Force FS choice */
  1690.     {    if (Ofs)
  1691.             FileSystem = IS_OFS;
  1692.         else
  1693.             FileSystem = IS_FFS;
  1694.     }
  1695.     else
  1696.     {    if (ShowFileSystem)
  1697.             printf("FileSystemHandler is \"%s\"\n", Stuff.Info.fsys);
  1698.  
  1699.         printf("Boot Block File System ... ");
  1700.         fflush(stdout);
  1701.  
  1702.         FileSystem = IS_FFS;    /* Assume - change if wrong */
  1703.  
  1704.         AkMotorOn(&Stuff);
  1705.         DoCache = !Seek;
  1706.         ret = CacheRead(Stuff.Info.Begin, &buf);
  1707.         AkMotorOff(&Stuff);
  1708.  
  1709.         if (ret)
  1710.             printf("Error 0x%lx on Read Block Zero - Assumed FFS\n", ret);
  1711.         else
  1712.         {    if ((*buf & 0xffffff00) != 0x444f5300)
  1713.                 printf("Doesn't appear to be DOS - Assumed FFS\n");
  1714.             else
  1715.             {    if ((*buf & 0xff) == 0)
  1716.                 {    FileSystem = IS_OFS;
  1717.                     printf("Appears to be OFS\n");
  1718.                 }
  1719.                 else
  1720.                     printf("Appears to be FFS or similar\n");
  1721.             }
  1722.         }
  1723.     }
  1724.     ValidateIt();
  1725. }
  1726.  
  1727. main(argc, argv)
  1728. int    argc;
  1729. char    *argv[];
  1730. {
  1731.     REG    int    idx;
  1732.  
  1733.     cli = argc;
  1734.  
  1735.     DO_QUAL();
  1736.  
  1737.     if (Cache > 1)
  1738.         Cache += 2;
  1739.  
  1740.     if (!Quiet && cli)
  1741.         STARTUP_MSG;
  1742.  
  1743.     OPEN_LIB(IBASE, "intuition.library", INTUITION_REV);
  1744.  
  1745.     PoolInit(&BufPoolKey, 0L, MEMF_CHIP);
  1746.     PoolInit(&GenPoolKey, PoolSize, 0L);
  1747.     PoolInit(&ZerPoolKey, 0L, MEMF_CLEAR);
  1748.  
  1749.     Val();
  1750.  
  1751.     if (StatCache)
  1752.     {    ulong    BTot = ReadSmall*2 + ReadLNum;
  1753.         ulong    RTot = HitSmall + HitLarge + ReadSmall + ReadLarge;
  1754.  
  1755. #define PER(x)  (RTot ? (double)(x) / (double)(RTot) * (double)(100): (double)0)
  1756.  
  1757.         printf("Cache Statistics (Real Time) - Cache = 2:%d\n", Cache-2);
  1758.         if (Cache == 1)
  1759.             puts("Well ... first you need to HAVE a cache! (Try /cache=75)");
  1760.         else
  1761.         {    printf("Blocks Requested....... %10lu\n", RTot);
  1762.             printf("Blocks Read............ %10lu\n", BTot);
  1763.             printf("Average Reads/Request.. %10.2lf\n", (double)(BTot) / (double)(RTot));
  1764.             printf("Total µsecs Cache Code. %10lu\n", MicCode);
  1765.             if (MicCode)
  1766.                 printf("Average µsec/CodeReq... %10.2lf\n",
  1767.                     (double)(MicCode) / (double)(HitSmall + HitLarge));
  1768.             printf("Total µsecs Cache IOReq %10lu\n", MicRead);
  1769.             if (MicRead)
  1770.                 printf("Average µsec/IOReq..... %10.2lf\n",
  1771.                     (double)(MicRead) / (double)(ReadSmall + ReadLarge));
  1772.             printf("Total µsec Cache Req... %10lu\n", MicCode + MicRead);
  1773.             if (MicCode || MicRead)
  1774.                 printf("Average µsec/Request... %10.2lf\n",
  1775.                     ((double)(MicCode) + (double)(MicRead)) / (double)(RTot));
  1776.             printf("Hits on Small Cache.... %10lu %6.2lf%%\n", HitSmall, PER(HitSmall));
  1777.             printf("Hits on Large Cache.... %10lu %6.2lf%%\n", HitLarge, PER(HitLarge));
  1778.             printf("Reads to Small Cache... %10lu %6.2lf%%\n", ReadSmall, PER(ReadSmall));
  1779.             printf("Reads to Large Cache... %10lu %6.2lf%%\n", ReadLarge, PER(ReadLarge));
  1780.         }
  1781.     }
  1782.  
  1783.     Cleanup();
  1784. }
  1785.  
  1786. MyError(num, why)
  1787. REG    long    num;
  1788. REG    char    *why;
  1789. {
  1790.     ERROR_INIT
  1791.  
  1792.     if (Inhibited)
  1793.         AkDevInhibit(&Stuff, 0L);
  1794.  
  1795.     if (!Dev)
  1796.         AkDevClose(&Stuff);
  1797.  
  1798.     ERROR_TEST
  1799.         CASE_ERR_TOODEEP
  1800.         CASE_ERR_AKDEVALL
  1801.         CASE_ERR_NOVAL
  1802.         CASE_ERR_NOMEM
  1803.         CASE_ERR_NOLIB
  1804.         CASE_ERR_BADFIL
  1805.     ERROR_END(FACILITY_VAL)
  1806.  
  1807.     PoolFree(&ZerPoolKey);
  1808.     PoolFree(&GenPoolKey);
  1809.     PoolFree(&BufPoolKey);
  1810.  
  1811.     if (IBASE)
  1812.         CloseLibrary(IBASE);
  1813.  
  1814.     CLEANUP_QUAL();
  1815.  
  1816.     ERROR_EXIT
  1817. }
  1818.